Java实现链式存储的二叉查找树(递归方法)
二叉查找树的定义:
二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树:
1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字;
2. 若右子树非空,则右子树上所有节点关键字值均大于根节点的关键字;
3. 左、右子树本身也分别是一颗二叉查找树。
二叉查找树的实现,功能有:
1. 用一个数组去构建二叉查找树
2. 二叉查找树的中序遍历和层次遍历
3. 插入节点
4. 查找节点
5. 查找二叉树中的最大值和最小值
6. 得到节点的直接父节点
7. 得到节点的直接前驱和直接后继节点
8. 删除节点
树节点TreeNode的定义见:Java实现链式存储的二叉树
1 import java.lang.Integer; 2 import java.util.LinkedList; 3 import java.util.Queue; 4 import java.util.Scanner; 5 6 /* 7 * 二叉排序树(二叉查找树)的实现 8 */ 9 public class BinarySearchTree { 10 private TreeNode<Integer> root; //根节点 11 12 public BinarySearchTree(){ 13 root = null; 14 } 15 16 //用一个数组去构建二叉查找树 17 public TreeNode<Integer> buildBST(Integer[] array){ 18 if(array.length == 0){ 19 return null; 20 }else{ 21 root = null; //初始化树为空树 22 for(int i=0; i<array.length; i++){ //依次将每个元素插入 23 root = insertNode(root,array[i]); 24 } 25 return root; 26 } 27 } 28 29 //在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点 30 public TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data){ 31 if(node == null){ //原树为空,新插入的记录为根节点 32 node = new TreeNode<Integer>(data,null,null); 33 }else{ 34 if(node.getData() == data){ //树中存在相同关键字的结点,什么也不做 35 36 }else{ 37 if(node.getData() > data){ //根节点>插入数据,插入到左子树中 38 node.setLchild(insertNode(node.getLchild(),data)); 39 }else{ //根节点<插入数据,插入到右子树中 40 node.setRchild(insertNode(node.getRchild(),data)); 41 } 42 } 43 } 44 return node; 45 } 46 47 //二叉查找树的中序遍历,可以得到一个递增的有序数列 48 public void inOrder(TreeNode<Integer> node){ 49 if(node != null){ 50 inOrder(node.getLchild()); 51 System.out.print(node.getData()+" "); 52 inOrder(node.getRchild()); 53 } 54 } 55 //二叉查找树的层次遍历 56 public void levelOrder(TreeNode<Integer> root){ 57 Queue<TreeNode<Integer>> nodeQueue = new LinkedList<TreeNode<Integer>>(); 58 TreeNode<Integer> node = null; 59 nodeQueue.add(root); //将根节点入队 60 while(!nodeQueue.isEmpty()){ //队列不空循环 61 node = nodeQueue.peek(); 62 System.out.print(node.getData()+" "); 63 nodeQueue.poll(); //队头元素出队 64 if(node.getLchild() != null){ //左子树不空,则左子树入队列 65 nodeQueue.add(node.getLchild()); 66 } 67 if(node.getRchild() != null){ //右子树不空,则右子树入队列 68 nodeQueue.add(node.getRchild()); 69 } 70 } 71 } 72 73 //查找数据域为data的结点,若不存在,返回null 74 public TreeNode<Integer> searchNode(TreeNode<Integer> node, Integer data){ 75 while(node != null && node.getData() != data){ 76 if(node.getData() > data){ 77 node = node.getLchild(); //根节点>数据,向左走 78 }else{ 79 node = node.getRchild(); //根节点<数据,向右走 80 } 81 } 82 return node; 83 } 84 85 //查找最大值:不断地寻找右子节点 86 public TreeNode<Integer> getMaxData(TreeNode<Integer> node){ 87 if(node.getRchild() == null){ 88 return node; 89 }else{ 90 return getMaxData(node.getRchild()); 91 } 92 } 93 94 //查找最小值:不断地寻找左子节点 95 public TreeNode<Integer> getMinData(TreeNode<Integer> node){ 96 if(node.getLchild() == null){ 97 return node; 98 }else{ 99 return getMinData(node.getLchild()); 100 } 101 } 102 103 //得到数据域为data的结点的直接父节点parentNode 104 public TreeNode<Integer> getParentNode(TreeNode<Integer> root, Integer data){ 105 TreeNode<Integer> parentNode = root; 106 if(parentNode.getData() == data){ //根节点的父节点返回为null 107 return null; 108 } 109 while(parentNode != null){ 110 //查找当前节点的父节点的左右子节点,若是相等,则返回该父节点 111 if((parentNode.getLchild() != null && parentNode.getLchild().getData() == data) || 112 (parentNode.getRchild() != null && parentNode.getRchild().getData() == data)){ 113 return parentNode; 114 }else{ 115 if(parentNode.getData() > data){ //向左查找父节点 116 parentNode = parentNode.getLchild(); 117 }else{ 118 parentNode = parentNode.getRchild(); //向右查找父节点 119 } 120 } 121 } 122 return null; 123 } 124 125 /** 126 * 得到结点node的直接前趋 127 * a.该节点左子树不为空:其前驱节点为其左子树的最大元素 128 * b.该节点左子树为空: 其前驱节点为其祖先节点(递归),且该祖先节点的右孩子也为其祖先节点 129 * (就是一直往其parent找,出现左拐后的那个祖先节点) 130 */ 131 public TreeNode<Integer> getPrecessor(TreeNode<Integer> root,TreeNode<Integer> node){ 132 if(node == null){ 133 return null; 134 } 135 //a.该节点左子树不为空:其前驱节点为其左子树的最大元素 136 if(node.getLchild() != null){ 137 return getMaxData(node.getLchild()); 138 }else{ //b.该节点左子树为空: 其前驱节点为其祖先节点(递归) 139 TreeNode<Integer> parentNode = getParentNode(root, node.getData()); 140 while(parentNode != null && node == parentNode.getLchild()){ 141 node = parentNode; 142 parentNode = getParentNode(root, parentNode.getData()); 143 } 144 return parentNode; 145 } 146 } 147 148 /** 149 * 得到结点node的直接后继(后继节点就是比要删除的节点的关键值要大的节点集合中的最小值) 150 * a.该节点右子树不为空,其后继节点为其右子树的最小元素 151 * b.该节点右子树为空,则其后继节点为其祖先节点(递归),且此祖先节点的左孩子也是该节点的祖先节点, 152 * 就是说一直往上找其祖先节点,直到出现右拐后的那个祖先节点: 153 */ 154 public TreeNode<Integer> getSuccessor(TreeNode<Integer> root,TreeNode<Integer> node){ 155 if(node == null){ 156 return null; 157 } 158 //a.该节点右子树不为空,其后继节点为其右子树的最小元素 159 if(node.getRchild() != null){ 160 return getMinData(node.getRchild()); 161 }else{ //b.该节点右子树为空,则其后继节点为其最高祖先节点(递归) 162 TreeNode<Integer> parentNode = getParentNode(root, node.getData()); 163 while(parentNode != null && node == parentNode.getRchild()){ 164 node = parentNode; 165 parentNode = getParentNode(root, parentNode.getData()); 166 } 167 return parentNode; 168 } 169 } 170 171 /** 172 * 删除数据域为data的结点 173 * 按三种情况处理: 174 * a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质 175 * b.如果节点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,代替z的位置 176 * c.若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z, 177 * 然后从二叉查找树中删去这个直接后继(或直接前驱),这样就转换为第一或第二种情况 178 * @param node 二叉查找树的根节点 179 * @param data 需要删除的结点的数据域 180 * @return 181 */ 182 public boolean deleteNode(TreeNode<Integer> node, Integer data){ 183 if(node == null){ //树为空 184 throw new RuntimeException("树为空!"); 185 } 186 TreeNode<Integer> delNode= searchNode(node, data); //搜索需要删除的结点 187 TreeNode<Integer> parent = null; 188 if(delNode == null){ //如果树中不存在要删除的关键字 189 throw new RuntimeException("树中不存在要删除的关键字!"); 190 }else{ 191 parent = getParentNode(node,data); //得到删除节点的直接父节点 192 //a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质 193 if(delNode.getLchild()==null && delNode.getRchild()==null){ 194 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子 195 parent.setLchild(null); 196 }else{ //被删除节点为其父节点的右孩子 197 parent.setRchild(null); 198 } 199 return true; 200 } 201 //b1.如果节点z只有一颗左子树,则让z的子树成为z父节点的子树,代替z的位置 202 if(delNode.getLchild()!=null && delNode.getRchild()==null){ 203 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子 204 parent.setLchild(delNode.getLchild()); 205 }else{ //被删除节点为其父节点的右孩子 206 parent.setRchild(delNode.getLchild()); 207 } 208 delNode.setLchild(null); //设置被删除结点的左孩子为null 209 return true; 210 } 211 //b2.如果节点z只有一颗右子树,则让z的子树成为z父节点的子树,代替z的位置 212 if(delNode.getLchild()==null && delNode.getRchild()!=null){ 213 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子 214 parent.setLchild(delNode.getRchild()); 215 }else{ //被删除节点为其父节点的右孩子 216 parent.setRchild(delNode.getRchild()); 217 } 218 delNode.setRchild(null); //设置被删除结点的右孩子为null 219 return true; 220 } 221 //c.若结点z有左、右两颗子树,则删除该结点的后继结点,并用该后继结点取代该结点 222 if(delNode.getLchild()!=null && delNode.getRchild()!=null){ 223 TreeNode<Integer> successorNode = getSuccessor(node,delNode); //得到被删除结点的后继节点 224 deleteNode(node,successorNode.getData()); //删除该结点的后继结点 225 delNode.setData(successorNode.getData()); //用该后继结点取代该结点 226 return true; 227 } 228 } 229 return false; 230 } 231 232 233 public static void main(String args[]){ 234 Scanner input = new Scanner(System.in); 235 Integer[] array = {8,3,10,1,6,14,4,7,13}; 236 BinarySearchTree bst = new BinarySearchTree(); 237 TreeNode<Integer> root = bst.buildBST(array); 238 System.out.print("层次遍历:"); 239 bst.levelOrder(root); 240 System.out.print("\n"+"中序遍历:"); 241 bst.inOrder(root); 242 System.out.println(); 243 System.out.print("得到最大值:"); 244 System.out.println(bst.getMaxData(root).getData()); 245 System.out.print("得到最小值:"); 246 System.out.println(bst.getMinData(root).getData()); 247 System.out.print("向二叉查找树中插入一个节点,请输入需插入节点的数据域:"); 248 int data = input.nextInt(); 249 System.out.print("插入节点"+ data +"后,中序遍历的结果:"); 250 root = bst.insertNode(root, data); 251 bst.inOrder(root); 252 System.out.println("\n"+"在二叉查找树中查找元素,"+"请输入需要查找的结点值:"); 253 data = input.nextInt(); 254 if(bst.searchNode(root, data) == null){ 255 System.out.println("false"); 256 }else{ 257 System.out.println("true"); 258 } 259 System.out.println("查找节点的直接父节点,"+"请输入需要查找的结点值:"); 260 data = input.nextInt(); 261 System.out.print("节点"+ data +"的父节点是:"); 262 if(bst.getParentNode(root, data) == null){ 263 System.out.println("null"); 264 }else{ 265 System.out.println(bst.getParentNode(root, data).getData()); 266 } 267 System.out.println("删除结点,"+"请输入需要删除的结点值:"); 268 data = input.nextInt(); 269 if(bst.deleteNode(root, data)){ 270 System.out.print("删除结点后的层次遍历:"); 271 bst.levelOrder(root); 272 System.out.print("\n"+"删除结点后的中序遍历:"); 273 bst.inOrder(root); 274 } 275 } 276 }
程序运行结果:
层次遍历:8 3 10 1 6 14 4 7 13 中序遍历:1 3 4 6 7 8 10 13 14 得到最大值:14 得到最小值:1 向二叉查找树中插入一个节点,请输入需插入节点的数据域:15 插入节点15后,中序遍历的结果:1 3 4 6 7 8 10 13 14 15 在二叉查找树中查找元素,请输入需要查找的结点值: 4 true 查找节点的直接父节点,请输入需要查找的结点值: 10 节点10的父节点是:8 删除结点,请输入需要删除的结点值: 4 删除结点后的层次遍历:8 3 10 1 6 14 7 13 15 删除结点后的中序遍历:1 3 6 7 8 10 13 14 15
某些方法的非递归实现:
1. 插入节点insertNode():
1 //在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点 2 public TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data){ 3 TreeNode<Integer> newNode = new TreeNode<Integer>(data,null,null); 4 TreeNode<Integer> tmpNode = node; //遍历节点 5 TreeNode<Integer> pnode = null; //记录当前节点的父节点 6 7 if(node == null){ //原树为空,新插入的记录为根节点 8 node = newNode; 9 return node; 10 } 11 while(tmpNode != null){ 12 pnode = tmpNode; 13 if(tmpNode.getData() == data){ //树中存在相同关键字的结点,什么也不做 14 return node; 15 }else{ 16 if(tmpNode.getData() > data){ //根节点>插入数据,插入到左子树中 17 tmpNode = tmpNode.getLchild(); 18 }else{ //根节点<插入数据,插入到右子树中 19 tmpNode = tmpNode.getRchild(); 20 } 21 } 22 } 23 if(pnode.getData() > data){ 24 pnode.setLchild(newNode); 25 }else{ 26 pnode.setRchild(newNode); 27 } 28 return node; 29 }
2. 二叉查找树的中序遍历:
1 //二叉查找树的中序遍历LNR,可以得到一个递增的有序数列 2 public void inOrder(TreeNode<Integer> node){ 3 Stack<TreeNode<Integer>> nodeStack = new Stack<TreeNode<Integer>>(); 4 TreeNode<Integer> tempNode = node; //遍历指针 5 while(tempNode != null || !nodeStack.isEmpty()){ 6 if(tempNode != null){ 7 nodeStack.push(tempNode); 8 tempNode = tempNode.getLchild(); 9 }else{ 10 tempNode = nodeStack.pop(); 11 System.out.print(tempNode.getData() + " "); 12 tempNode = tempNode.getRchild(); 13 } 14 } 15 }
3. 得到二叉查找树的最大值和最小值:
1 //查找最大值:不断地寻找右子节点 2 public TreeNode<Integer> getMaxData(TreeNode<Integer> node){ 3 TreeNode<Integer> tempNode = node; 4 while(tempNode.getRchild()!=null){ 5 tempNode = tempNode.getRchild(); 6 } 7 return tempNode; 8 } 9 10 //查找最小值:不断地寻找左子节点 11 public TreeNode<Integer> getMinData(TreeNode<Integer> node){ 12 TreeNode<Integer> tempNode = node; 13 while(tempNode.getLchild() != null){ 14 tempNode = tempNode.getLchild(); 15 } 16 return tempNode; 17 }